<?php

/**
 * Base CRUD Table builder
 *
 * @todo Improve selective field rendering
 *
 * Example usage
 * echo $this->tableBuilder(oRowSet, array(
 *	'header' => true,
 *  'sorter' => true,
 * 	'fields' => array(
 *		'firstname' => 'Firstname',
 *		'surname' => 'Surname',
 *		'email' => 'Email'
 *	),
 *  'linkSeparator' => '|',
 *  'rowCycle' => array('aclass','bclass'...)
 *	'rowLinks' => array(
 *		array(
 *			'controller' => 'member',
 *			'action' => 'update',
 *			'label' => 'Edit'
 *		),
 *		array(
 *			'controller' => 'member',
 *			'action' => 'view',
 *			'label' => 'View'
 *		)
 *	)
 * ));
 */

class Zend_View_Helper_TableBuilder extends Zend_View_Helper_Abstract
{
	const LINK_SEPARATOR = ' | ';

	/**
	 * A list of fields that the user wishes to render
	 *
	 * @var array
	 */
	protected $_fieldsToRender = array();

	/**
	 *
	 *
	 * @var array
	 */
	protected $_rowCycle = array('odd','even');

	public function tableBuilder($oRowSet, array $aOptions = array())
	{
		$sOutput = '<table class="'.
			(array_key_exists('tableClass', $aOptions) ? $aOptions['tableClass'] : null ).
		'">';

		// Sort out which fields we need to render
		if(array_key_exists('fields', $aOptions) && is_array($aOptions['fields']))
			$this->_makeFieldsToRender($oRowSet, $aOptions['fields']);

		if (array_key_exists('header', $aOptions) && $aOptions['header'] === true)
			$sOutput .= $this->_addHeader($oRowSet, $aOptions);

		$c = $this->view->getHelper('Cycle');
		foreach ($oRowSet as $aRow)
		{
			if (array_key_exists('rowCycle', $aOptions))
				$this->_rowCycle = $aOptions['rowCycle'];

			$sOutput .= '<tr class="'.$c->cycle($this->_rowCycle)->next().'">';
			foreach ($this->_fieldsToRender as $sField => $value)
			{
				$sOutput .= "<td>{$aRow[$sField]}</td>";
			}
			
			if (array_key_exists('rowLinks', $aOptions))
			{
				$aLinks = array();
				foreach($aOptions['rowLinks'] as $aLink)
					$aLinks[] = $this->_addLink($aRow, $aLink);

				$linkSeparator = (array_key_exists('linkSeparator', $aOptions) ? $aOptions['linkSeparator'] : self::LINK_SEPARATOR);
				$sOutput .= '<td>' . implode($linkSeparator, $aLinks) . '</td>';
			}

			$sOutput .= "</tr>";
		}
		$sOutput .= "</table>";
		return $sOutput;
	}

	/**
	 * Works out which fields that ned to be rendered and stores them in the object
	 *
	 * @param object $oRowSet
	 * @param array $aFields
	 */

	protected function _makeFieldsToRender($oRowSet, array $aFields = array())
	{
		if($oRowSet instanceof Zend_Paginator)
			$aFirstRow = $oRowSet->getItem(0);

		if($oRowSet instanceof Zend_Db_Table)
			$aFirstRow = $oRowSet[0];

		if (isset($aFields))
			$this->_fieldsToRender = $aFields;
		else
			$this->_fieldsToRender = array_keys($aFirstRow);
	}

	/**
	 * Builds a link at the end of the table that associates with the row
	 *
	 * @param array $aRow
	 * @param array $aLink
	 * @return string
	 */

	protected function _addLink($aRow, array $aLink)
	{
		$aRequiredKeys = array('controller','action','label');
		foreach( $aRequiredKeys as $key )
			if(!array_key_exists($key, $aLink))
				throw new Exception ("Missing required key [{$key}] for tableBuilder link");

		$u = $this->view->getHelper('Url');
		$sOutput = '<a href="';
		$sOutput .= $u->url(array(
			'controller' => $aLink['controller'],
			'action' => $aLink['action'],
			'id' => (array_key_exists('id', $aLink) ? $aRow[$aLink['id']] : $aRow['id']),
		), 'default', true );
		$sOutput .= '">'.$aLink['label'].'</a>';
		return $sOutput;
	}

	/**
	 * Uses the keys from the first record in the set to build a header
	 *
	 * @param object $oRowSet
	 * @return string
	 */

	protected function _addHeader($oRowSet, array $aOptions = array())
	{
		if($oRowSet instanceof Zend_Paginator)
			$aFirstRow = $oRowSet->getItem(0);

		if($oRowSet instanceof Zend_Db_Table)
			$aFirstRow = $oRowSet[0];

		$sOutput = "<thead><tr>";

		foreach ($this->_fieldsToRender as $k => $v)
		{
			if(array_key_exists('sorter', $aOptions) && $aOptions['sorter'] === true)
			{
				$sorter = $this->view->getHelper('SortOrder');
				$sOutput .= '<td>'.$sorter->sortOrder($k, $v).'</td>';
			} else
				$sOutput .= "<td>{$v}</td>";
		}
		$sOutput .= "</tr></thead>\n";
		return $sOutput;
	}
}